﻿using System.Collections;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Web;
using System.Web.Mvc;
using ilgolosone.Persistence.NH.Data;
using ilgolosone.Persistence.NH.Data.Impl;
using ilgolosone.Shared.Data;
using Microsoft.Practices.Unity;
using NUnit.Framework;

namespace ilgolosone.DependencyResolver.Unity.UnitTests
{
    [TestFixture]
    public class ContainerFixture
    {
        private IUnityContainer _container;
        private RegisteredTypesExtension _registrations;

        [SetUp]
        public void TestFixtureSetUp()
        {
            _container = new UnityContainer();
            _registrations = new RegisteredTypesExtension();
            _container.AddExtension(_registrations);
            _container.ConfigureWithRegistry(new DefaultRegistry());
        }

        [TearDown]
        public void TestFixtureTearDown()
        {
            _container.Dispose();
            _container = null;
        }

        [Test]
        public void verifica_tipi_registrati_nel_container()
        {
            _registrations.HasImplementationsFor<IControllerFactory>().Should().Be.True();
            _registrations.HasImplementationsFor<ISessionCreator>().Should().Be.True();
            _registrations.HasImplementationsFor<IUnitOfWork>().Should().Be.True();
            _registrations.HasImplementationsFor(typeof(IRepository<>)).Should().Be.True();
        }

        [Test]
        public void la_unitofwork_deve_essere_quella_per_nhibernate()
        {
            using (var uow = _container.Resolve<IUnitOfWork>())
            {
                uow.Should().Be.OfType<NHibernateUnitOfWork>();
            }
        }
        
        [Test]
        public void nello_stesso_thread_di_esecuzione_la_unitofwork_deve_essere_la_stessa()
        {
            var uow1 = _container.Resolve<IUnitOfWork>();
            var uow2 = _container.Resolve<IUnitOfWork>();
            Assert.AreEqual(uow1, uow2);
            Assert.AreSame(uow1, uow2);
            uow1.Dispose();
            uow2.Dispose();
        }

        [Test]
        public void in_diversi_thread_di_esecuzione_la_unitofwork_deve_essere_diversa()
        {
            IUnitOfWork uow1 = null;
            IUnitOfWork uow2 = null;

            RunInParallel(
                () => uow1 = _container.Resolve<IUnitOfWork>(),
                () => uow2 = _container.Resolve<IUnitOfWork>()
            );

            uow1.Should().Not.Be.Null();
            uow2.Should().Not.Be.Null();

            Assert.AreNotEqual(uow1, uow2);
            Assert.AreNotSame(uow1, uow2);

            uow1.Dispose();
            uow2.Dispose();
        }

        [Test]
        public void il_sessioncreator_deve_essere_singleton()
        {
            ISessionCreator sessionCreator1 = null;
            ISessionCreator sessionCreator2 = null;

            RunInParallel(
                () => sessionCreator1 = _container.Resolve<ISessionCreator>(),
                () => sessionCreator2 = _container.Resolve<ISessionCreator>()
            );

            sessionCreator1.Should().Not.Be.Null();
            sessionCreator2.Should().Not.Be.Null();

            Assert.AreEqual(sessionCreator1, sessionCreator2);
            Assert.AreSame(sessionCreator1, sessionCreator2);
        }

        [Test]
        public void la_controllerfactory_deve_essere_singleton()
        {
            IControllerFactory cf1 = null;
            IControllerFactory cf2 = null;

            RunInParallel(
                () => cf1 = _container.Resolve<IControllerFactory>(),
                () => cf2 = _container.Resolve<IControllerFactory>()
            );

            cf1.Should().Not.Be.Null();
            cf2.Should().Not.Be.Null();

            Assert.AreEqual(cf1, cf2);
            Assert.AreSame(cf1, cf2);
        }

        [Test]
        public void in_webapp_le_istanze_stanno_in_httpcontext()
        {
            using (var simulator = new HttpSimulator("/", Assembly.GetCallingAssembly().Location))
            {
                using (simulator.SimulateRequest())
                {
                    // Riconfigura il container dopo aver instanziato il contesto web
                    _container.Dispose();
                    _container = new UnityContainer();
                    _container.ConfigureWithRegistry(new DefaultRegistry());

                    using (var uow = _container.Resolve<IUnitOfWork>())
                    {
                        uow.Start();

                        (from DictionaryEntry de in HttpContext.Current.Items
                         where de.Value != null && de.Value.GetType() == typeof (NHibernateUnitOfWork)
                         select de).Count().Should().Be.EqualTo(1);
                    }
                }
            }
        }

        private static void RunInParallel(params ThreadStart[] actions)
        {
            var threads = actions.Select(action => new Thread(action)).ToList();

            // Start them all...
            threads.ForEach(t => t.Start());

            // And wait for them all to finish
            threads.ForEach(t => t.Join());
        }
    }
}
